package code

import (
	"go/ast"
	"reflect"
	"strings"
)

// Structs is a group of Struct model
type Structs []*Struct

// ByName return struct with matching name. Another return value shows whether there is a struct
// with that name exists.
func (strs Structs) ByName(name string) (*Struct, bool) {
	for _, str := range strs {
		if str.Name == name {
			return str, true
		}
	}
	return nil, false
}

// Struct is a definition of the struct
type Struct struct {
	PackageAlias string
	Name         string
	Fields       StructFields
}

// ReferencedType returns a type variable of this struct
func (str Struct) ReferencedType() Type {
	if str.PackageAlias == "" {
		return SimpleType(str.Name)
	}
	return ExternalType{Name: str.Name, PackageAlias: str.PackageAlias}
}

// StructFields is a group of the StructField model
type StructFields []StructField

// ByName return struct field with matching name
func (fields StructFields) ByName(name string) (StructField, bool) {
	for _, field := range fields {
		if field.Name == name {
			return field, true
		}
	}
	return StructField{}, false
}

// StructField is a definition of the struct field
type StructField struct {
	Name string
	Type Type
	Tags reflect.StructTag
}

func extractStructTag(tagValue string) reflect.StructTag {
	tagValue = strings.Trim(tagValue, "`")
	st := reflect.StructTag(tagValue)
	return st
}

func extractStructType(name string, packageAlias string, structType *ast.StructType) *Struct {
	str := Struct{
		Name:         name,
		PackageAlias: packageAlias,
	}

	for _, field := range structType.Fields.List {
		var strField StructField
		for _, name := range field.Names {
			strField.Name = name.Name
			break
		}
		strField.Type = getType(field.Type)
		if field.Tag != nil {
			strField.Tags = extractStructTag(field.Tag.Value)
		}

		str.Fields = append(str.Fields, strField)
	}

	return &str
}
